#include <stdlib.h>
#include <stdio.h>
#include "calque.h"
#include "image.h"
#include "interface.h"

/* Fonctions principales */

ListeCalques newCalque(ListeCalques c){
	c = (Calque*)malloc(sizeof(Calque));
	if(c == NULL){
		printf("Erreur d'allcocation\n");
		exit(1);
	}
	else{
		c->image = background(512, 512);
		c->source = -1;
		c->key = -1;
		c->h = c->image->nbLines;
		c->w = c->image->nbColumns;
		c->prevCalque = NULL;
		c->nextCalque = NULL;
		c->opacity = 1;
		c->mix = 1;
		c->llut = (Lut*)malloc(sizeof(Lut)*10);
	}
	return c;
}

ListeCalques createCalque(Storage* storage, ListeCalques c){
	c = (Calque*)malloc(sizeof(Calque));
	if (c == NULL){
		printf("Erreur d'allocation\n");
		exit(1);
	}
	else{
		c->image = background(storage->album[storage->lastKey].nbColumns, storage->album[storage->lastKey].nbLines); //Prend la taille de l'image donné en argument
		c->source = -1;
		c->key = -1;
		c->w = c->image->nbColumns;
		c->h = c->image->nbLines;
		c->nextCalque = NULL;
		c->prevCalque = NULL;
		c->opacity = 1;
		c->mix = 1;
		c->llut = (Lut*)malloc(sizeof(Lut)*10);
	}
	return addCalque(storage, c);
}

ListeCalques addCalque(Storage* storage, ListeCalques c){
	Calque* lc = NULL;
	Calque* calque = NULL;
	int i,j,k = 0;

	lc = lastCalque(c);
	calque = (Calque*)malloc(sizeof(Calque));
	if (calque == NULL){
		printf("Erreur d'allocation\n");
	}
	else{
		/* copie du storage dans le calque */
		calque->image = (Image*)malloc(sizeof(Image));
		if (calque->image == NULL) {
			printf("Erreur d'allocation dans mémoire\n");
			exit(1);
		}
		calque->image->type = (unsigned char*)malloc(sizeof(unsigned char)*2);
		if (calque->image->type == NULL) {
			printf("Erreur d'allocation dans mémoire\n");
			exit(1);
		}
		calque->key = storage->lastKey;
		calque->image->type[0] = storage->album[calque->key].type[0];
		calque->image->type[1] = storage->album[calque->key].type[1];
		calque->image->nbColumns = storage->album[calque->key].nbColumns;
		calque->image->nbLines = storage->album[calque->key].nbLines;
		calque->image->maxValueColor = storage->album[calque->key].maxValueColor;
		calque->image->pixels = (unsigned char*)malloc(sizeof(unsigned char)*(calque->image->nbColumns)*(calque->image->nbLines)*3);
		if (calque->image->pixels == NULL) {
			printf("Erreur d'allocation dans mémoire\n");
			exit(1);
		}

		for(i = 0;i<(calque->image->nbLines);i++) {
			for(j = 0;j<(calque->image->nbColumns*3);j++) {
				calque->image->pixels[k++] = (unsigned char)(int)(storage->album[calque->key].pixels[i*calque->image->nbColumns*3+j]);
			}
		}

		calque->source = 1;
		calque->mix = 1;
		calque->w = c->w;
		calque->h = c->h;
		calque->llut = NULL;
		calque->opacity = 1;
		calque->nextCalque = NULL;
		calque->prevCalque = lc;
		lc->nextCalque = calque;
	}
	//printf("Tout s'est reREbien reREpassé %d ! type : %s - %d - %d - %d - %d\n", calque->image, calque->image->type, calque->image->nbColumns, calque->image->nbLines, calque->image->maxValueColor, calque->image->pixels); //test
	return c; //Pas obligé de return vu qu'on change pas l'adresse de c qui est le pointeur de la liste de calque.
}

unsigned char* changeView(ListeCalques l, Storage* storage){
	unsigned char* img_base = NULL;
	printf("source : %d\n", l->source);
	if(l->source == -1){
		printf("Vous etes sur le calque blanc, il n'y a pas d'image source.\n");
		img_base = inverseImg(l->image);
	}
	else if(l->source == 1){
		printf("Image source.\n");
		l->source = 0;
		img_base = inverseImg(&(storage->album[l->key]));
	}
	else{
		printf("Image finale.\n");
		l->source = 1;
		img_base = inverseImg(l->image);
	}
	return img_base;
}

void afficherCalques(ListeCalques l, Storage* storage){
	int i = 0;
	int key = 0;
	Calque* c = l;

	while(c->prevCalque != NULL){
		c = c->prevCalque;
	}
	while(c != NULL){
		key = getKey(c);
		printf("adresse du %d er/eme calque: %d | %d - %d - %d - %d\n", i, c, c->image->type, c->image->nbColumns, c->image->nbLines, c->image->maxValueColor, c->image->pixels);
		if(key != -1){
			printf("%d er/eme image source : %d - %d - %d - %d\n\n", key, storage->album[key].type, storage->album[key].nbColumns, storage->album[key].nbLines, storage->album[key].maxValueColor, storage->album[key].pixels);
		}
		else{
			printf("Calque blanc\n\n");
		}
		c = c->nextCalque;
		i++;
	}
}

ListeCalques nextCalque(ListeCalques l){ //Attention nextCalque n'atteint jamais NULL.
	if(l->nextCalque != NULL){
		l = l->nextCalque;
		return l;
	}
	else{
		return l;
	}
}

ListeCalques prevCalque(ListeCalques l){
	if(l->prevCalque != NULL){
		l = l->prevCalque;
		return l;
	}
	else{
		return l;
	}
}

ListeCalques lastCalque(ListeCalques l){
	Calque* c = l;
	while(c->nextCalque != NULL){
		c = c->nextCalque;
	}
	return c;
}

ListeCalques firstCalque(ListeCalques l){
	Calque* c = l;
	while(c->prevCalque != NULL){
		c = c->prevCalque;
	}
	return c;
}

int getKey(ListeCalques l){
	return l->key;
}

unsigned char* switchCalque(ListeCalques* l, Storage* storage){
	unsigned char* img_base = NULL;
	if((*l)->nextCalque != NULL){
		*l = nextCalque(*l);
		if((*l)->source == 0){
			// printf("Image source.\n");
			img_base = inverseImg(&(storage->album[(*l)->key]));
		}
		else{
			// printf("Image finale.\n");
			img_base = inverseImg((*l)->image);
		}
	}
	else{
		*l = firstCalque(*l);
		if((*l)->source == 0){
			// printf("Image source.\n");
			img_base = inverseImg(&(storage->album[(*l)->key]));
		}
		else{
			// printf("Image finale.\n");
			img_base = inverseImg((*l)->image);
		}
	}
	return img_base;
}

ListeCalques delCalque(ListeCalques l){
	ListeCalques s;
	ListeCalques p;
	ListeCalques suppr;

	s = NULL;
	p = NULL;
	suppr = NULL;

	if(l->prevCalque == NULL){
		return l;
	}
	else if(l->nextCalque == NULL){
		suppr = l;
		l = prevCalque(l);
		l->nextCalque = NULL;
		free(suppr);
		suppr = NULL;
	}	
	else{
		suppr = l;
		prevCalque(l)->nextCalque = nextCalque(l);
		nextCalque(l)->prevCalque = prevCalque(l);
		l = prevCalque(l);
		free(suppr);
		suppr = NULL;
	}
	return l;
}

/*   Fonctions secondaires   */

void switchMix(Calque* c){
	if (c->mix == 0){
		c->mix = 1;
		printf("%d\n", c->mix);
	}
	else{
		c->mix = 0;	
		printf("%d\n", c->mix);
		// return inverseImg(c->image)	;
	}
}

unsigned char* increaseOpacity(Calque* c, Storage* storage){
	int i,j;
	unsigned char pixel_source = 0;
	Calque* b = NULL;

	if(c->prevCalque != NULL){
		printf("increase opacity\n");
		if(c->opacity < 1.0001 && c->opacity > 0.9999){
			printf("vous etes deja au max de l'opacite\n");
			return inverseImg(c->image);
		}
		else if(c->opacity >= (-0.0001) && c->opacity <= 1){
			c->opacity = c->opacity + 0.1;
			printf("opacite %f\n", c->opacity);
			// if (c->mix == 0){

			// 	if (c->prevCalque != NULL){
			// 		b = c->prevCalque;

			// 		for(i = 0; i < (c->image->nbLines); i++){
			// 			for(j = 0; j < (c->image->nbColumns*3); j++){
			// 				//Cf = Cn-1 + α * Cn
			// 				pixel_source = (storage->album[c->key].pixels[i*storage->album[c->key].nbColumns*3+j]);
			// 				c->image->pixels[i*c->image->nbColumns*3+j] = b->image->pixels[i*c->image->nbColumns*3+j] + (c->opacity) * pixel_source ;
			// 			}
			// 		}
			// 		updateCalque(c, storage);
			// 	}
			// }
			// else if(c->mix == 1){
				
			// 	if (c->prevCalque != NULL){
			// 		b = c->prevCalque;

			// 		for(i = 0; i < (c->image->nbLines); i++){
			// 			for(j = 0; j < (c->image->nbColumns*3); j++){
			// 				//Cn = (1 − α) ∗ Cn−1 + α ∗ Cn
			// 				pixel_source = (storage->album[c->key].pixels[i*storage->album[c->key].nbColumns*3+j]);
			// 				c->image->pixels[i*c->image->nbColumns*3+j] = ((1- c->opacity) * b->image->pixels[i*c->image->nbColumns*3+j]) + (c->opacity)* pixel_source;
			// 			}
			// 		}
			// 		updateCalque(c, storage);
			// 	}
			// }
		updateCalque(c, storage);
		}
		return inverseImg(c->image);
		
	}
	else{
		printf("Vous ne pouvez pas modifier l'opacité du calque blanc\n");
		return inverseImg(c->image);
	}
}

unsigned char* decreaseOpacity(Calque* c, Storage* storage){
	int i,j;
	unsigned char pixel_source = 0;
	Calque* b = NULL;

	if(c->prevCalque != NULL){
		printf("decrease\n");
		if(c->opacity <= 0.0001 && c->opacity >= (-0.0001)){
			printf("vous etes deja au minimum de l'opacite\n");
		}
		else if(c->opacity >= 0 && c->opacity <= 1){
			c->opacity = c->opacity - 0.1;
			printf("opacite %f\n", c->opacity);
			updateCalque(c, storage);
		}
		return inverseImg(c->image);
	}
	else{
		printf("Vous ne pouvez pas modifier l'opacité du calque blanc\n");
		return inverseImg(c->image);
	}	
}

void updateCalque(Calque* c, Storage* storage){
	Calque* temp = c;
	Calque* p = NULL;
	unsigned char pixel_source = 0; 
	int i,j;

	printf("test avant while\n");
	while(temp != NULL){
		printf("test while\n");
		if (temp->mix == 0){
			if (c->prevCalque != NULL){
					p = c->prevCalque;
					for(i = 0; i < (temp->image->nbLines); i++){
						for(j = 0; j < (temp->image->nbColumns*3); j++){
							//Cf = Cn-1 + α * Cn
							pixel_source = (storage->album[temp->key].pixels[i*storage->album[temp->key].nbColumns*3+j]);
							temp->image->pixels[i*temp->image->nbColumns*3+j] = p->image->pixels[i*temp->image->nbColumns*3+j] + (temp->opacity) * pixel_source;
						}
					}
			}

		}
		else{
			if(c->prevCalque != NULL){
				p = c->prevCalque;
				for(i = 0; i < (temp->image->nbLines); i++){
					for(j = 0; j < (temp->image->nbColumns*3); j++){
						//formule produit
						pixel_source = (storage->album[temp->key].pixels[i*storage->album[temp->key].nbColumns*3+j]);
						temp->image->pixels[i*temp->image->nbColumns*3+j] = ((1- temp->opacity) * p->image->pixels[i*temp->image->nbColumns*3+j]) + (temp->opacity) * pixel_source;
					}
				}
			}
		}
		temp = temp->nextCalque;
	}
	printf("fin up\n");
}

void resetImg(Storage* storage, Calque* c){
	int i,j,k = 0;
	for(i = 0;i<(c->image->nbLines);i++) {
		for(j = 0;j<(c->image->nbColumns*3);j++) {
			c->image->pixels[k++] = (unsigned char)(int)(storage->album[c->key].pixels[i*c->image->nbColumns*3+j]);
		}
	}
	updateCalque(c,storage);
}

/* Fonctions LUT */

void refreshLut(ListeLut l, Calque* c){
	if(l != NULL){
		ListeLut temp = l;
		while(temp != NULL){
			setLut(temp, c);
			temp = temp->nextLut;
		}
	}
}

void setLut(ListeLut l, Calque* c){
	int i;
	int length = (c->image->nbLines) * (c->image->nbColumns) * 3;
	printf("test setlut\n");

	for(i=0; i<length; i+=3) {
		c->image->pixels[i]=(unsigned char)(l->tabR[c->image->pixels[i]]);
		c->image->pixels[i+1]=(unsigned char)(l->tabG[c->image->pixels[i+1]]);
		c->image->pixels[i+2]=(unsigned char)(l->tabB[c->image->pixels[i+2]]);
	}
}

ListeLut delLut(ListeLut l){
	if(l != NULL){
		ListeLut t0 = NULL;
		ListeLut t1 = l;

		while(t1->nextLut != NULL){
			t0 = t1;
			t1 = t1->nextLut;
		}

		if(t0 != NULL){
			t0->nextLut = NULL;
			if(t1 != NULL){
				free(t1->tabR);
				free(t1->tabG);
				free(t1->tabB);
				free(t1);
			}
		}
		else{
			free(l->tabR);
			free(l->tabG);
			free(l->tabB);
			free(l);
			l = NULL;
			printf("toto\n");
		}
		return l;
	}
	else{
		printf("Il n'y a plus de lut a supprimer\n");
		return l;
	}
}

void greyScale(Calque* c){
	int i;
	unsigned char grey;

	for (i = 0; i < c->image->nbColumns*c->image->nbLines*3; i+=3){
		grey = (c->image->pixels[i] * 0.299+c->image->pixels[i+1] * 0.587+c->image->pixels[i+2] * 0.114);
		c->image->pixels[i] = grey;
		c->image->pixels[i+1] = grey;
		c->image->pixels[i+2] = grey;
	}
}